package com.gxtbindings.showcase.client.demo;

import java.util.Arrays;
import java.util.List;

import com.extjs.gxt.ui.client.data.BeanModel;
import com.extjs.gxt.ui.client.data.BeanModelFactory;
import com.extjs.gxt.ui.client.data.BeanModelLookup;
import com.extjs.gxt.ui.client.event.ButtonEvent;
import com.extjs.gxt.ui.client.event.SelectionListener;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.store.StoreEvent;
import com.extjs.gxt.ui.client.store.StoreListener;
import com.extjs.gxt.ui.client.widget.Html;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.button.Button;
import com.extjs.gxt.ui.client.widget.form.NumberField;
import com.extjs.gxt.ui.client.widget.form.TextField;
import com.extjs.gxt.ui.client.widget.grid.CellEditor;
import com.extjs.gxt.ui.client.widget.grid.ColumnConfig;
import com.extjs.gxt.ui.client.widget.grid.ColumnModel;
import com.extjs.gxt.ui.client.widget.grid.EditorGrid;
import com.extjs.gxt.ui.client.widget.grid.Grid;
import com.extjs.gxt.ui.client.widget.layout.TableData;
import com.extjs.gxt.ui.client.widget.layout.TableLayout;
import com.google.gwt.user.client.Random;
import com.gxtbindings.bindings.client.BeanModelConverter;
import com.gxtbindings.bindings.client.ListStoreBinding;
import com.gxtbindings.bindings.client.util.EventList;
import com.gxtbindings.bindings.client.util.EventListListener;
import com.gxtbindings.bindings.client.util.ListChangeEvent;

public class BindingDemo extends LayoutContainer {

	private Grid<BeanModel> grid;
	
	private EventList<Person> list;
	
	private ListStoreBinding<Person,BeanModel> binding;
	
	public static String[] firstNames = {
		"Michael",
		"Christopher",
		"Matthew",
		"Jason",
		"David",
		"James",
		"Joshua",
		"John",
		"Robert",
		"Daniel"
	};

	public static String[] lastNames = {
		"Smith",
		"Johnson",
		"Williams",
		"Jones",
		"Brown",
		"Davis",
		"Miller",
		"Wilson",
		"Moore",
		"Taylor"
	};

	public BindingDemo() {
		initComponents();
		layoutContainer();
	}

	private void initComponents() {
		grid = new EditorGrid<BeanModel>(new ListStore<BeanModel>(), createColumnModel());
		grid.setAutoHeight(true);
		list = new EventList<Person>();
		
		binding = new ListStoreBinding<Person, BeanModel>(grid.getStore());
		binding.setConverter(new BeanModelConverter<Person>());
		binding.bind(list);
	}

	private void layoutContainer() {
		TableLayout layout = new TableLayout(3);
		layout.setCellSpacing(10);
		layout.setCellPadding(8);
		layout.setBorder(1);
		setLayout(layout);
		
		add(new Html("EventList"));
		add(new Html("Grid (Click cell to modify)"));
		add(new Html("Items will only show up as modified or removed after a commit"));
		
		add(createListViewContainer());
		add(createGridContainer());
		add(createBottomContainer());
//		add(createListViewContainer(), new TableData("100%", null));
//		add(createGridContainer(), new TableData("100%", null));
		
//		TableData td = new TableData();
//		td.setColspan(2);
//		add(createBottomContainer(), td);
	}
	
	private LayoutContainer createBottomContainer() {
		LayoutContainer c = new LayoutContainer(new TableLayout(4));
		
		final Html recordsView = new Html();
		recordsView.setTagName("span");
		TableData td = new TableData();
		td.setColspan(4);
		c.add(recordsView, td);
		
		recordsView.setHtml(getRecordsString());
		
//		c.add(new Button("Load initial state", new SelectionListener<ButtonEvent>() {
//			public void componentSelected(ButtonEvent ce) {
//				list.clear();
//				
//				List<Person> aList = new ArrayList<Person>();
//				for (int i = 0; i < 4; i++)
//				{
//					aList.add(new Person(first(), last(), age()));
//				}
//				list.addAll(aList);
//				binding.commitChanges();
//				
//				recordsView.setHtml(getRecordsString());
//			}
//		}));
//		
//		c.add(new Button("View New/Modified/Removed", new SelectionListener<ButtonEvent>() {
//			public void componentSelected(ButtonEvent ce) {
//				recordsView.setHtml(getRecordsString());
//			}
//		}));
		
		c.add(new Button("Commit", new SelectionListener<ButtonEvent>() {
			public void componentSelected(ButtonEvent ce) {
				binding.commitChanges();
				recordsView.setHtml(getRecordsString());
			}
		}));
		
		c.add(new Button("Reject", new SelectionListener<ButtonEvent>() {
			public void componentSelected(ButtonEvent ce) {
				binding.rejectChanges();
				recordsView.setHtml(getRecordsString());
			}
		}));
		
		// The following are for display purposes only. List is already in sync
		list.addEventListListener(new EventListListener<Person>() {
			public void listChanged(ListChangeEvent<Person> ev) {
				recordsView.setHtml(getRecordsString());
			}
		});
	
		grid.getStore().addStoreListener(new StoreListener<BeanModel>() {

			@Override
			public void storeUpdate(StoreEvent<BeanModel> se) {
				recordsView.setHtml(getRecordsString());
			}
		});
		
		return c;
	}

	private LayoutContainer createGridContainer() {
		LayoutContainer c = new LayoutContainer(new TableLayout(1));
		c.add(grid);
		
		final BeanModelFactory factory = BeanModelLookup.get().getFactory(Person.class);
		
		// Add buttons
		c.add(new Button("Add", new SelectionListener<ButtonEvent>() {
			public void componentSelected(ButtonEvent ce) {
				Person p = new Person(first(), last(), age());
				grid.getStore().add(factory.createModel(p));
			}
		}));
		
		c.add(new Button("Insert", new SelectionListener<ButtonEvent>() {
			public void componentSelected(ButtonEvent ce) {
				Person p = new Person(first(), last(), age());
				grid.getStore().insert(factory.createModel(p), Random.nextInt(grid.getStore().getCount()));
			}
		}));
//		
//		c.add(new Button("Delete First", new SelectionListener<ButtonEvent>() {
//			public void componentSelected(ButtonEvent ce) {
//				grid.getStore().remove(0);
//			}
//		}));
//		
//		c.add(new Button("Delete Last", new SelectionListener<ButtonEvent>() {
//			public void componentSelected(ButtonEvent ce) {
//				grid.getStore().remove(grid.getStore().getCount() - 1);
//			}
//		}));
//		
		c.add(new Button("Remove Random", new SelectionListener<ButtonEvent>() {
			public void componentSelected(ButtonEvent ce) {
				if (grid.getStore().getCount() > 0)
				{
					grid.getStore().remove(grid.getStore().getAt(Random.nextInt(grid.getStore().getCount())));
				}
			}
		}));
		
		c.add(new Button("Remove all", new SelectionListener<ButtonEvent>() {
			public void componentSelected(ButtonEvent ce) {
				grid.getStore().removeAll();
			}
		}));
		return c;
	}

	private LayoutContainer createListViewContainer()
	{
		LayoutContainer c = new LayoutContainer(new TableLayout(1));
		final Html listView = new Html();
		listView.setTagName("span");
		c.add(listView);
		
		// Add buttons
		c.add(new Button("Add", new SelectionListener<ButtonEvent>() {
			public void componentSelected(ButtonEvent ce) {
				list.add(new Person(first(), last(), age()));
			}
		}));
		
		c.add(new Button("Insert", new SelectionListener<ButtonEvent>() {
			public void componentSelected(ButtonEvent ce) {
				list.add(Random.nextInt(list.size()), new Person(first(), last(), age()));
			}
		}));
		
		c.add(new Button("Set", new SelectionListener<ButtonEvent>() {
			public void componentSelected(ButtonEvent ce) {
				list.set(Random.nextInt(list.size()), new Person(first(), last(), age()));
			}
		}));
		
//		c.add(new Button("Delete First", new SelectionListener<ButtonEvent>() {
//			public void componentSelected(ButtonEvent ce) {
//				list.remove(0);
//			}
//		}));
//		
//		c.add(new Button("Delete Last", new SelectionListener<ButtonEvent>() {
//			public void componentSelected(ButtonEvent ce) {
//				list.remove(list.size() - 1);
//			}
//		}));
		
		c.add(new Button("Remove Random", new SelectionListener<ButtonEvent>() {
			public void componentSelected(ButtonEvent ce) {
				if (list.size() > 0)
				{
					list.remove(Random.nextInt(list.size()));
				}
			}
		}));
		
		c.add(new Button("Clear", new SelectionListener<ButtonEvent>() {
			public void componentSelected(ButtonEvent ce) {
				list.clear();
			}
		}));
		
		// The following are for display purposes only. List is already in sync
		list.addEventListListener(new EventListListener<Person>() {
			public void listChanged(ListChangeEvent<Person> ev) {
				listView.setHtml(getListAsString(list));
			}
		});
	
		grid.getStore().addStoreListener(new StoreListener<BeanModel>() {

			@Override
			public void storeUpdate(StoreEvent<BeanModel> se) {
				listView.setHtml(getListAsString(list));
			}
		});
		return c;
	}
	
	private String first()
	{
		return firstNames[Random.nextInt(firstNames.length)];
	}
	
	private String last()
	{
		return lastNames[Random.nextInt(lastNames.length)];
	}
	
	private int age()
	{
		return Random.nextInt(100) + 1;
	}
	
	public String getListAsString(List<Person> aList)
	{
		StringBuilder sb = new StringBuilder();
		int i = 0;
		for (Person person : aList)
		{
			sb.append("<span style=\"font-size: 11px;\">" + person.toString() + "</span");
			if (i != aList.size() - 1)
			{
				sb.append("<br/>");
			}
			i++;
		}
		return sb.toString();
	}

	public String getRecordsString()
	{
		StringBuilder sb = new StringBuilder();
		sb.append("<span style=\"font-size: 11px;\">New: <br/>" + getListAsString(binding.getNewObjects()) + "<br/></span>");
		sb.append("<span style=\"font-size: 11px;\"><br/>Modified: <br/>" + getListAsString(binding.getModifiedObjects()) + "<br/></span>");
		sb.append("<span style=\"font-size: 11px;\"><br/>Removed: <br/>" + getListAsString(binding.getRemovedObjects()) + "<br/></span>");
		return sb.toString();
	}
	
	
	private static ColumnModel createColumnModel() {
		ColumnConfig[] cols = {
				new ColumnConfig("firstName", "First Name", 100),
				new ColumnConfig("lastName", "Last Name", 100),
				new ColumnConfig("age", "Age", 100),
		};
		cols[0].setEditor(new CellEditor(new TextField<String>()));
		cols[1].setEditor(new CellEditor(new TextField<String>()));
		cols[2].setEditor(new IntegerCellEditor());
		return new ColumnModel(Arrays.asList(cols));
	}
	
	public static class IntegerCellEditor extends CellEditor
	{
		public IntegerCellEditor() {
			super(new NumberField());
		}

		public Object postProcessValue(Object value) {
			if (value == null)
			{
				return null;
			}
			else
			{
				return ((Number)value).intValue();
			}
		}
	}
	
}
